package com.iflytek.jzcpx.procuracy.ocr.service.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.base.Joiner;
import com.iflytek.jzcpx.procuracy.ocr.common.constant.Constants;
import com.iflytek.jzcpx.procuracy.ocr.common.enums.RecognizeFileStatusEnum;
import com.iflytek.jzcpx.procuracy.ocr.common.enums.RecognizeFuncEnum;
import com.iflytek.jzcpx.procuracy.ocr.common.enums.TaskFileTypeEnum;
import com.iflytek.jzcpx.procuracy.ocr.dao.RecognizeFileDao;
import com.iflytek.jzcpx.procuracy.ocr.entity.RecognizeFile;
import com.iflytek.jzcpx.procuracy.ocr.service.RecognizeFileService;
import com.iflytek.jzcpx.procuracy.tools.common.PropUtils;
import com.iflytek.sxs.dfs.client.FdfsClient;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

/**
 * @author <a href=mailto:ktyi@iflytek.com>伊开堂</a>
 * @date 2019-08-12 19:29
 */
@Service
public class RecognizeFileServiceImpl extends ServiceImpl<RecognizeFileDao, RecognizeFile>
        implements RecognizeFileService {
    private static final Logger logger = LoggerFactory.getLogger(RecognizeFileServiceImpl.class);

    @Autowired
    RecognizeFileDao recognizeFileDao;

    @Autowired
    private FdfsClient fdfsClient;
    @Override
    public Long createSingle(List<RecognizeFuncEnum> funcs) {
        RecognizeFile file = new RecognizeFile();
        Date now = new Date();
        file.setCreateTime(now);
        file.setUpdateTime(now);
        file.setType(TaskFileTypeEnum.SINGLE.toString());
        file.setStatus(RecognizeFileStatusEnum.INIT.toString());
        file.setFuncList(Joiner.on(",").join(funcs));
        file.setState(Constants.STATE_NOT_DELETED);
        boolean saveSuccess = save(file);
        logger.info("保存单文件图像识别记录, funcs[ {} ]", funcs);
        return saveSuccess ? file.getId() : null;
    }

    @Override
    public boolean abnormalEnd(Long fileId, String errorInfo) {
        RecognizeFile params = new RecognizeFile();
        Date now = new Date();
        params.setUpdateTime(now);
        params.setId(fileId);
        params.setStatus(RecognizeFileStatusEnum.END.toString());
        params.setFailInfo(errorInfo);
        logger.info("图像识别任务异常结束, fileId[ {} ], errorInfo[ {} ]", fileId, errorInfo);
        return updateById(params);
    }

    @Override
    public boolean updateStatus(Long fileId, RecognizeFileStatusEnum targetStatusEnum) {
        RecognizeFile params = new RecognizeFile();
        Date now = new Date();
        params.setUpdateTime(now);
        params.setId(fileId);
        params.setStatus(targetStatusEnum.toString());
        logger.info("更新图像识别文件状态, fileId[ {} ], targetStatusEnum[ {} ]", fileId, targetStatusEnum);
        return updateById(params);
    }

    @Override
    public boolean updateEngineResultPath(Long fileId, String storePath) {
        RecognizeFile params = new RecognizeFile();
        Date now = new Date();
        params.setUpdateTime(now);
        params.setId(fileId);
        params.setStatus(RecognizeFileStatusEnum.ENGINE_RESULT_STORE.toString());
        params.setEngineResultPath(storePath);
        logger.info("更新 ocr 引擎识别结果存储位置, fileId[ {} ], storePath[ {} ]", fileId, storePath);
        return updateById(params);
    }

    @Override
    public Map<Long, List<Long>> findUndoneFileIdMap(List<Long> recognizeTaskIdList) {
        if (CollectionUtils.isEmpty(recognizeTaskIdList)) {
            return null;
        }

        List<RecognizeFile> dataList = new ArrayList<>();
        int size = recognizeTaskIdList.size();
        int step = PropUtils.getInt("undone_file_step", 50);
        int lastIndex = 0;
        try {
            do {
                int endIndex = Math.min(lastIndex + step, size);
                List<Long> taskIds = recognizeTaskIdList.subList(lastIndex, endIndex);
                List<RecognizeFile> undoneFiles = findUndoneFiles(taskIds);
                if (CollectionUtils.isNotEmpty(undoneFiles)) {
                    dataList.addAll(undoneFiles);
                }
                lastIndex = endIndex;
            } while (lastIndex <= size - 1);
        }
        catch (Exception e) {
            logger.warn("SQL查询异常", e);
        }

        if (CollectionUtils.isEmpty(dataList)) {
            return null;
        }

        return dataList.stream().collect(Collectors.groupingBy(RecognizeFile::getRecognizeTaskId,
                                                               Collectors.mapping(RecognizeFile::getId,
                                                                                  Collectors.toList())));
    }

    private List<RecognizeFile> findUndoneFiles(List<Long> recognizeTaskIdList) {
        if (recognizeTaskIdList.isEmpty()) {
            return new ArrayList<>();
        }
        LambdaQueryWrapper<RecognizeFile> wrapper = new LambdaQueryWrapper<>();
        wrapper.select(RecognizeFile::getId, RecognizeFile::getRecognizeTaskId);
        wrapper.in(RecognizeFile::getRecognizeTaskId, recognizeTaskIdList);
        wrapper.ne(RecognizeFile::getStatus, RecognizeFileStatusEnum.END.name());
        return list(wrapper);
    }

    @Override
    public List<RecognizeFile> listByBsbhWjxh(String bsbh, List<String> wjxhList) {
        if (CollectionUtils.isEmpty(wjxhList) && StringUtils.isNotBlank(bsbh)) {
            return new ArrayList<>();
        }
        LambdaQueryWrapper<RecognizeFile> wrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotBlank(bsbh)) {
            wrapper.eq(RecognizeFile::getBsbh, bsbh);
        }
        if (CollectionUtils.isNotEmpty(wjxhList)) {
            wrapper.in(RecognizeFile::getWjxh, wjxhList);
        }
        return list(wrapper);
    }

    @Override
    public int cleanData(Date startDatetime, Date endDatetime, boolean cleanDB, boolean cleanFdfs) {
        if (!cleanDB && !cleanFdfs) {
            logger.warn("即不清理DB, 又不清理fastDFS, 你清理个毛啊");
            return 0;
        }

        logger.info("清理recognizeFile数据, 起止时间: {} - {}", startDatetime, endDatetime);
        Assert.isTrue(startDatetime != null || endDatetime != null, "开始和结束时间不能同时为空");

        int cleanCount = 0;

        List<RecognizeFile> recognizeFiles = null;
        int pageSize = 100;
        int pageNum = 1;
        do {
            Page<RecognizeFile> page = new Page<>(pageNum, pageSize);
            LambdaQueryWrapper<RecognizeFile> wrapper = new LambdaQueryWrapper<>();
            wrapper.select(RecognizeFile::getId, RecognizeFile::getOcrResultPath, RecognizeFile::getEngineResultPath);
            if (startDatetime != null) {
                wrapper.gt(RecognizeFile::getCreateTime, startDatetime);
            }
            if (endDatetime != null) {
                wrapper.lt(RecognizeFile::getCreateTime, endDatetime);
            }
            IPage<RecognizeFile> recognizeFilePage = this.baseMapper.selectPage(page, wrapper);
            if (recognizeFilePage == null || CollectionUtils.isEmpty(recognizeFilePage.getRecords())) {
                logger.info("没有待清理的recognizeFile数据了");
                return cleanCount;
            }

            recognizeFiles = recognizeFilePage.getRecords();
            logger.debug("查询到待清理的recognizeFile数据, size: {}, page: {}", recognizeFiles.size(), page);

            for (RecognizeFile recognizeFile : recognizeFiles) {
                if (deleteDataAndFile(recognizeFile, cleanDB, cleanFdfs)) {
                    cleanCount++;
                }
            }

            if (!cleanDB) {
                pageNum++;
            }
        } while (CollectionUtils.size(recognizeFiles) >= pageSize);

        return cleanCount;
    }

    private boolean deleteDataAndFile(RecognizeFile recognizeFile, boolean cleanDB, boolean cleanFdfs) {
        logger.info("删除recognizeFile数据, recognizeFile: {}", recognizeFile);
        if (cleanDB) {
            logger.info("删除recognizeFile数据库数据, recognizeFileId: {}", recognizeFile.getId());
            removeById(recognizeFile);
        }
        if (cleanFdfs) {
            String ocrResultPath = recognizeFile.getOcrResultPath();
            String engineResultPath = recognizeFile.getEngineResultPath();
            if (StringUtils.isNotBlank(ocrResultPath)) {
                logger.info("删除fastDFS文件, recognizeFileId: {}, ocrResultPath: {}", recognizeFile.getId(), ocrResultPath);
                try {
                    fdfsClient.deleteFile(ocrResultPath);
                }
                catch (Exception e) {
                    logger.warn("删除fastDFS文件异常, recognizeFileId: {}, ocrResultPath: {}, errorInfo: {}", recognizeFile.getId(),
                                ocrResultPath, e.getMessage());
                }
            }

            if (StringUtils.isNotBlank(engineResultPath)) {
                logger.info("删除fastDFS文件, recognizeFileId: {}, engineResultPath: {}", recognizeFile.getId(), engineResultPath);
                try {
                    fdfsClient.deleteFile(engineResultPath);
                }
                catch (Exception e) {
                    logger.warn("删除fastDFS文件异常, recognizeFileId: {}, engineResultPath: {}, errorInfo: {}", recognizeFile.getId(),
                                engineResultPath, e.getMessage());
                }
            }

        }
        return true;
    }
}
